package org.kalisen.classpathdoctor.gui; import java.awt.Color; import java.awt.Component; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceAdapter; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDropEvent; import java.io.IOException; import java.util.List; import java.util.Vector; import javax.swing.DefaultListCellRenderer; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListModel; import org.kalisen.classpathdoctor.EmptyPathEntry; import org.kalisen.classpathdoctor.PathEntry; import org.kalisen.common.DefaultErrorHandler; import org.kalisen.common.ErrorHandler; public class ClassPathList extends JList { private ErrorHandler errorHandler = null; private final DragGestureListener listDragGestureListener = new DragGestureListener() { public void dragGestureRecognized(DragGestureEvent dge) { JList list = (JList) dge.getComponent(); Object[] selection = list.getSelectedValues(); if (selection.length < 2) { PathEntry[] selectedEntries = new PathEntry[selection.length]; System.arraycopy(selection, 0, selectedEntries, 0, selection.length); PathEntriesTransferable transferable = new PathEntriesTransferable( selectedEntries); dge.startDrag(DragSource.DefaultMoveDrop, transferable); } else { getErrorHandler().handleError( "Only one entry can be moved at a time"); } } }; private final DragSourceListener listDragSourceListener = new DragSourceAdapter() { public void dragDropEnd(DragSourceDropEvent dsde) { if (dsde.getDropSuccess()) { JList list = (JList) dsde.getDragSourceContext().getComponent(); int[] selectedIndices = list.getSelectedIndices(); ((ClassPathListModel) list.getModel()) .removeElementAt(selectedIndices[0]); list.setSelectedIndex(ClassPathList.this.listDropTargetListener .getFirstDropIndex()); list.repaint(); } } }; private final ClassPathListDropTargetListener listDropTargetListener = new ClassPathListDropTargetListener(); /** * Constructs a <code>ClassPathList</code> that displays the elements in the * specified, non-<code>null</code> model. All <code>ClassPathList</code> * constructors delegate to this one. * * @param dataModel * the data model for this list * @exception IllegalArgumentException * if <code>dataModel</code> is <code>null</code> */ public ClassPathList(ClassPathListModel dataModel) { super(dataModel); setCellRenderer(new ClassPathListCellRenderer()); // set Drag enable to false as it is related to swing DnD support setDragEnabled(false); System.setProperty("awt.dnd.drag.threshold", "1"); DragSource dSource = new DragSource(); dSource.addDragSourceListener(this.listDragSourceListener); dSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this.listDragGestureListener); DropTarget dt = new DropTarget(this, this.listDropTargetListener); setDropTarget(dt); } /** * Constructs a <code>ClassPathList</code> that displays the elements in the * specified array. This constructor just delegates to the * <code>ListModel</code> constructor. * * @param listData * the array of Objects to be loaded into the data model */ public ClassPathList(final PathEntry[] listData) { this(new ClassPathListModel() { public int getSize() { return listData.length; } public PathEntry getElementAt(int i) { return listData[i]; } }); } /** * Constructs a <code>ClassPathList</code> that displays the elements in the * specified <code>Vector</code>. This constructor just delegates to the * <code>ListModel</code> constructor. * * @param listData * the <code>Vector</code> to be loaded into the data model */ public ClassPathList(final Vector<PathEntry> listData) { this(new ClassPathListModel() { public int getSize() { return listData.size(); } public PathEntry getElementAt(int i) { return listData.elementAt(i); } }); } /** * Constructs a <code>ClassPathList</code> with an empty model. */ public ClassPathList() { this(new ClassPathListModel()); } @Override public ClassPathListModel getModel() { return (ClassPathListModel) super.getModel(); } @Override public void setModel(ListModel model) { if (model instanceof ClassPathListModel) { super.setModel(model); } else { throw new IllegalArgumentException("Expected an instance of " + ClassPathListModel.class.getName() + " but was " + (model == null ? null : model.getClass().getName())); } } @Override public void setListData(Object[] listData) { if (listData instanceof PathEntry[]) { super.setListData(listData); } else { throw new IllegalArgumentException("Expected an instance of " + PathEntry[].class.getName() + " but was " + (listData == null ? null : listData.getClass().getName())); } } public void setListData(final PathEntry[] listData) { setModel(new ClassPathListModel() { public int getSize() { return listData.length; } public PathEntry getElementAt(int i) { return listData[i]; } }); } public void setListData(final Vector<?> listData) { for (Object object : listData) { if (!(object instanceof PathEntry)) { throw new IllegalArgumentException("Expected a Vector of " + PathEntry.class.getName() + " but contained instance of " + (object == null ? null : object.getClass().getName())); } } setModel(new ClassPathListModel() { public int getSize() { return listData.size(); } public PathEntry getElementAt(int i) { return (PathEntry) listData.get(i); } }); } public void setListData(final List<PathEntry> listData) { setModel(new ClassPathListModel() { public int getSize() { return listData.size(); } public PathEntry getElementAt(int i) { return listData.get(i); } }); } public ErrorHandler getErrorHandler() { if (this.errorHandler == null) { this.errorHandler = new DefaultErrorHandler(); } return this.errorHandler; } public void setErrorHandler(ErrorHandler errorHandler) { if (errorHandler == null) { throw new IllegalArgumentException("null is not a valid argument"); } this.errorHandler = errorHandler; } private class ClassPathListCellRenderer extends DefaultListCellRenderer { private final Color ODD_COLOR = new Color(0xEEEFFF); private final Color NONEXIST_COLOR = new Color(0xFF0000); public ClassPathListCellRenderer() { super(); } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { ClassPathList cpList = (ClassPathList) list; Component result = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); PathEntry entry = cpList.getModel().getElementAt(index); setComponentBackground(entry, index, isSelected, result); setComponentText(entry, result); return result; } protected void setComponentText(PathEntry entry, Component toBeModified) { if (EmptyPathEntry.INSTANCE.equals(entry)) { ((JLabel) toBeModified).setText(" "); } } protected void setComponentBackground(PathEntry entry, int index, boolean isSelected, Component toBeModified) { if (entry.exists() || EmptyPathEntry.INSTANCE.equals(entry)) { if (!isSelected && index % 2 == 1 && !toBeModified.getBackground().equals(this.ODD_COLOR)) { toBeModified.setBackground(this.ODD_COLOR); } } else if (!isSelected && !entry.exists()) { toBeModified.setBackground(this.NONEXIST_COLOR); } } } public static class PathEntriesTransferable implements Transferable { public static final DataFlavor PATHENTRIES_DATAFLAVOR = new DataFlavor( PathEntry[].class, "Path Entries"); private static final DataFlavor[] FLAVORS = { PATHENTRIES_DATAFLAVOR }; private PathEntry[] entries = null; public PathEntriesTransferable(PathEntry[] entries) { if (entries == null) { throw new IllegalArgumentException( "null is not a valid argument"); } this.entries = new PathEntry[entries.length]; System.arraycopy(entries, 0, this.entries, 0, entries.length); } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (!isDataFlavorSupported(flavor)) { throw new UnsupportedFlavorException(flavor); } return this.entries; } public DataFlavor[] getTransferDataFlavors() { return FLAVORS; } public boolean isDataFlavorSupported(DataFlavor flavor) { return FLAVORS[0].equals(flavor); } } private class ClassPathListDropTargetListener extends DropTargetAdapter { private int firstDropIndex = -1; public void drop(DropTargetDropEvent dtde) { boolean acceptDrop = dtde.isLocalTransfer() && dtde .isDataFlavorSupported(PathEntriesTransferable.PATHENTRIES_DATAFLAVOR) && dtde.getDropAction() == DnDConstants.ACTION_MOVE; if (acceptDrop) { dtde.acceptDrop(DnDConstants.ACTION_MOVE); dtde.dropComplete(doDrop(dtde)); } else { dtde.rejectDrop(); } } public boolean doDrop(DropTargetDropEvent dtde) { boolean dropSucceeded = true; Transferable transferable = dtde.getTransferable(); PathEntry[] droppedEntries; try { droppedEntries = (PathEntry[]) transferable .getTransferData(PathEntriesTransferable.PATHENTRIES_DATAFLAVOR); Point dropLocation = dtde.getLocation(); JList list = (JList) dtde.getDropTargetContext().getComponent(); this.firstDropIndex = list.locationToIndex(dropLocation); if (list.getSelectedIndex() < this.firstDropIndex) { ((ClassPathListModel) list.getModel()).insertElementAt( this.firstDropIndex + 1, droppedEntries[0]); } else { ((ClassPathListModel) list.getModel()).insertElementAt( this.firstDropIndex, droppedEntries[0]); } } catch (Exception e) { dropSucceeded = false; } return dropSucceeded; } public int getFirstDropIndex() { return this.firstDropIndex; } } }